﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using SerialBrowserBridge.Devices;
using SerialBrowserBridge.WebServer.Handler;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace SerialBrowserBridge.WebServer
{
    public class WebServerCore: IDisposable
    {
        private IBlockDevice _device;
        public IBlockDevice Device
        {
            get { return _device; }
            set { _device = value; }
        }

        private List<IWebHandler> webHandlers;

        /// <summary>
        /// Initialize web server and start listening
        /// </summary>
        public void Initalize()
        {
            // Get all handlers
            var handlers = from handler in Assembly.GetExecutingAssembly().GetTypes()
                           where handler.GetInterfaces().Contains(typeof(IWebHandler))
                           select (IWebHandler)Activator.CreateInstance(handler);
            
            // Sort priority & initalize
            webHandlers = handlers.OrderBy(o => o.Priority).ToList();
            webHandlers.ForEach(e => e.Initialize(this));

            // Start listener
            listner = new TcpListener(new IPEndPoint(IPAddress.Any, 0));
            listner.Start(10);

            // Start connection thread
            connectionThread = new Thread(HandleConnections);
            connectionThread.Start();
        }

        private TcpListener listner;
        private Thread connectionThread;
        public String URL
        {
            get { return listner == null ? "" : String.Concat("http://localhost:", ((IPEndPoint)listner.LocalEndpoint).Port); }
        }

        /// <summary>
        /// Handle connections, build request/response object and send 
        /// response back
        /// </summary>
        private void HandleConnections(object state)
        {
            while (Thread.CurrentThread.IsAlive)
            {
                // Get connection to client
                var client = listner.AcceptTcpClient();
                var stream = client.GetStream();
                try
                {

                    // Read complete HTTP-Request
                    var requestBuffer = new StringBuilder();
                    var buffer = new byte[512];
                    var length = buffer.Length;
                    do
                    {
                        length = stream.Read(buffer, 0, buffer.Length);
                        requestBuffer.Append(Encoding.Default.GetString(buffer, 0, length));
                    } while (client.Available != 0);
                    var requestString = requestBuffer.ToString();

                    // Create request-object
                    var request = Request.Parse(requestString);

                    // Create response-object
                    var response = new Response();

                    if (request != null)
                    {
                        Object result;
                        // Remember: Ordering is reverse
                        var handlerPipeline = new Stack<IWebHandler>(webHandlers);
                        while (handlerPipeline.Count > 0)
                        {
                            // process handler
                            result = handlerPipeline.Pop().Process(request, response);

                            // redirection?
                            if (result is RedirectAction)
                            {
                                // create new request
                                request = new Request{ URL = (result as RedirectAction).URL };

                                handlerPipeline = new Stack<IWebHandler>(webHandlers);
                            }

                            // request handled?
                            if (result is HandleAction)
                            {
                                if ((result as HandleAction).Handled == true)
                                    break;
                            }
                        }
                    }

                    if (!response.HasResponse)
                    {
                        response.MimeType = "text/html";
                        response.Write("<html><body>Keine Behandlung der Anfrage</body></html>");
                    }

                    // Send response to client
                    response.Render(stream);
                }
                catch (Exception)
                {
                    // TODO Log exception 
                } 
                
                stream.Close();
            }
        }


        public void Dispose()
        {
            listner.Stop();
            connectionThread.Abort();
        }
    }
}
